<template>
  <div class="file-upload">
    <div class="file-upload-container" @click.prevent="triggerUpload" v-bind="$attrs">
      <slot v-if="fileStatus === 'loading'" name="loading">
        <button class="btn btn-primary" disabled>正在上传...</button>
      </slot>
      <slot v-else-if="fileStatus === 'success'" name="uploaded" :uploadedData="uploadedData">
        <button class="btn btn-primary">上传成功</button>
      </slot>
      <slot v-else name="default">
        <button class="btn btn-primary">点击上传</button>
      </slot>
    </div>
    <input class="file-input d-none" type="file" ref="fileInput" @change="handleFileChange" />
  </div>
</template>

<script lang="ts">
export default {
  inheritAttrs: false,
};
</script>

<script lang="ts" setup>
import { defineProps, defineEmits, ref, PropType } from "vue";
import { UploadStatus, BeforeUploadFunction } from "@/types";
import axios from "axios";

const props = defineProps({
  action: {
    type: String,
    required: true,
  },
  beforeUpload: {
    type: Function as PropType<BeforeUploadFunction>,
  },
});

const emit = defineEmits(["file-uploaded", "file-uploaded-error"]);

const fileInput = ref<null | HTMLInputElement>(null);
const fileStatus = ref<UploadStatus>("ready");
// 插槽 prop:https://v3.vuejs.org/guide/component-slots.html#scoped-slots
const uploadedData = ref();

const triggerUpload = () => {
  if (fileInput.value) {
    fileInput.value.click();
  }
};

const handleFileChange = (e: Event) => {
  // https://developer.mozilla.org/zh-CN/docs/Web/API/File/Using_files_from_web_applications
  const currentTarget = e.target as HTMLInputElement;
  if (currentTarget.files) {
    const files = Array.from(currentTarget.files);

    if (props.beforeUpload) {
      const result = props.beforeUpload(files[0]);
      if (!result) return;
    }

    fileStatus.value = "loading";
    // https://developer.mozilla.org/zh-CN/docs/Web/API/FormData
    const formData = new FormData();
    formData.append("file", files[0]);
    axios
      .post(props.action, formData, { headers: { "Content-Type": "multipart/form-data" } })
      .then((res) => {
        fileStatus.value = "success";
        uploadedData.value = res.data;
        emit("file-uploaded", res.data);
      })
      .catch((error) => {
        fileStatus.value = "error";
        emit("file-uploaded-error", { error });
      })
      .finally(() => {
        fileInput.value && (fileInput.value.value = "");
      });
  }
};
</script>
